from clickhouse_connect import get_client
from clickhouse_connect.driver.client import Client
from typing import List, Dict, Optional, Any, ClassVar
import logging
from contextlib import contextmanager
import threading
from app.config.clickhouse_config import click_config

logger = logging.getLogger(__name__)


class ClickHouseHTTPPool:
    """
    ClickHouse 连接池 (线程安全的单例模式)
    优化点：
    1. 使用类变量实现单例
    2. 增加线程锁保证线程安全
    3. 懒加载机制避免过早初始化
    4. 连接池自动维护
    """
    _instance: ClassVar[Optional['ClickHouseHTTPPool']] = None
    _lock: ClassVar[threading.Lock] = threading.Lock()

    def __new__(cls):
        """单例模式实现"""
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        """懒加载初始化"""
        if self._initialized:
            return

        self._pool: List[Client] = []
        self._pool_lock = threading.Lock()
        self._config = {
            'host': click_config.CLICKHOUSE_HOST,
            'port': click_config.CLICKHOUSE_PORT,
            'username': click_config.CLICKHOUSE_USER,
            'password': click_config.CLICKHOUSE_PASSWORD,
            'database': click_config.CLICKHOUSE_DB,
            'connect_timeout': 10,
            'settings': {
                'max_execution_time': 30,
                'send_timeout': 30,
                'receive_timeout': 20
            }
        }
        self._pool_size = click_config.CLICKHOUSE_POOL_SIZE
        self._initialized = True
        logger.info("ClickHouse连接池配置加载完成")

    def _create_connection(self) -> Client:
        """创建新连接（带异常处理）"""
        try:
            conn = get_client(**self._config)
            logger.debug("创建新ClickHouse连接成功")
            return conn
        except Exception as e:
            logger.error(f"创建ClickHouse连接失败: {str(e)}")
            raise

    def _init_pool(self):
        """初始化连接池（线程安全）"""
        with self._pool_lock:
            if not self._pool:
                self._pool = [self._create_connection() for _ in range(self._pool_size)]
                logger.info(f"初始化连接池完成，共{self._pool_size}个连接")

    @contextmanager
    def get_connection(self) -> Client:
        """
        获取连接（自动回收）
        用法：
        with pool.get_connection() as conn:
            conn.query(...)
        """
        self._init_pool()  # 确保连接池已初始化

        conn = None
        try:
            with self._pool_lock:
                conn = self._pool.pop() if self._pool else self._create_connection()
            yield conn
        except Exception as e:
            logger.error(f"获取连接失败: {str(e)}")
            if conn:
                try:
                    conn.close()
                except Exception:
                    pass
            raise
        finally:
            if conn:
                with self._pool_lock:
                    if len(self._pool) < self._pool_size:
                        self._pool.append(conn)
                    else:
                        try:
                            conn.close()
                        except Exception:
                            pass

    def execute(self,
                query: str,
                params: Optional[Dict] = None,
                settings: Optional[Dict] = None) -> List[Dict[str, Any]]:
        """
        执行查询（线程安全）
        :param query: SQL查询语句
        :param params: 查询参数
        :param settings: 自定义设置（覆盖默认）
        """
        final_settings = {**self._config['settings'], **(settings or {})}
        with self.get_connection() as conn:
            try:
                result = conn.query(query, parameters=params, settings=final_settings)
                return [dict(zip(result.column_names, row)) for row in result.result_rows]
            except Exception as e:
                logger.error(f"查询执行失败: {str(e)}")
                raise

    def close_all(self):
        """关闭所有连接（线程安全）"""
        with self._pool_lock:
            while self._pool:
                conn = self._pool.pop()
                try:
                    conn.close()
                except Exception as e:
                    logger.warning(f"关闭连接失败: {str(e)}")
            logger.info("所有ClickHouse连接已关闭")


# 全局单例访问点
def get_clickhouse_pool() -> ClickHouseHTTPPool:
    """获取全局连接池单例（推荐使用此方法而不是直接访问实例）"""
    return ClickHouseHTTPPool()
